/****************************************************************
 * <strong>MFDataAdapter适配器基类</strong>
 *
 * <p>
 * 适配器基类
 * </p>
 *
 * @author AndyZhang 2011 cnet2001@163.com
 * @version 0.1
 * ToDo:
 ***************************************************************/
package com.thinkwide.data.ormapping;

import com.thinkwide.data.core.MFString;
import com.thinkwide.data.ormapping.datatable.MFDataColumnException;
import com.thinkwide.data.ormapping.datatable.MFDataRow;
import com.thinkwide.data.ormapping.datatable.MFDataState;
import com.thinkwide.data.ormapping.datatable.MFDataTable;
import com.thinkwide.data.ormapping.datatable.MFDataUpdateType;
import com.thinkwide.data.ormapping.db.MFConnectionInfo;
import com.thinkwide.data.ormapping.db.MFConnectionPool;
import com.thinkwide.data.ormapping.db.MFDataManager;
import com.thinkwide.data.ormapping.sql.MFSqlBase;
import com.thinkwide.data.ormapping.sql.MFSqlDelete;
import com.thinkwide.data.ormapping.sql.MFSqlInsert;
import com.thinkwide.data.ormapping.sql.MFSqlSelect;
import com.thinkwide.data.ormapping.sql.MFSqlUpdate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 数据适配器
 * 支持spring
 */
public class MFDataAdapter {
    private static Logger logger = LoggerFactory.getLogger(MFDataAdapter.class);
    private MFTransaction transaction;
    private String dbName = "";

    public MFDataAdapter() {
        transaction = new MFTransaction(MFConnectionPool.DEFAULT);
    }

    public MFDataAdapter(String dbName) {
        this(dbName, false);
    }

    public MFDataAdapter(MFTransaction transaction) {
        this.transaction = transaction;
    }

    public MFDataAdapter(Connection conn, MFConnectionInfo connInfo) {
        transaction = new MFTransaction(conn, connInfo);
    }

    public MFDataAdapter(String dbName, boolean beginTrans) {
        dbName = MFString.nullOrEmptyToString(dbName, MFConnectionPool.DEFAULT);

        transaction = new MFTransaction(dbName);
        this.dbName = dbName;
        if (beginTrans) {
            transaction.beginTransaction();
        }
    }

    public void free() throws SQLException {
        if (transaction != null) {
            transaction.free();
            transaction = null;
        }
    }

    public void close() throws SQLException {
        if (transaction != null) {
            transaction.free();
            transaction = null;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        free();
    }

    public List<String> getPrimaryKeys(String table) throws SQLException {
        ResultSet rs = null;
        List<String> list = new ArrayList<String>();

        rs = this.getTransaction().getConn().getMetaData().getPrimaryKeys(null, null, table);

        while (rs.next()) {
            String col = rs.getString("COLUMN_NAME");
            list.add(col);
        }

        if (rs != null) {
            rs.close();
        }

        return list;

    }

    public List<Object> getDataRowValue(MFDataRow row, List<String> fields) throws MFDataColumnException {
        List<Object> list = new ArrayList<Object>();
        for (String item : fields) {
            list.add(row.getValue(item));
        }
        return list;
    }

    public int insert(Object obj) throws Throwable {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(obj.getClass());
        String tableName = cInfo.getTableName();
        Map<String, String> ls = MFDataTransfer.getFieldPropList(obj);
        String strFields = MFString.Empty;
        String strValues = MFString.Empty;
        List<Object> argls = new ArrayList<Object>();
        for (Map.Entry<String, String> item : ls.entrySet()) {
            if (MFString.notNullOrEmpty(strFields)) {
                strFields = strFields + MFSqlBase.COMMA + item.getKey();
                strValues = strValues + MFSqlBase.COMMA + MFSqlBase.PARAM;
            } else {
                strFields = item.getKey();
                strValues = MFSqlBase.PARAM;
            }
            Object value = MFReflect.getProperty(obj, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlInsert.getInsertSql(tableName, strFields, strValues);

        return this.execSql(sSQL, argls.toArray());

    }

    public <T> int delete(Object obj) throws Throwable {

        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(obj.getClass());
        String tableName = cInfo.getTableName();
        String where = MFString.Empty;
        List<Object> argls = new ArrayList<Object>();
        Map<String, String> ls = MFDataTransfer.getPrimaryKeyFieldList(obj.getClass());
        for (Map.Entry<String, String> item : ls.entrySet()) {

            if (MFString.notNullOrEmpty(where)) {
                where = where + MFSqlBase.AND;
            }
            where = where + item.getKey() + "= ? ";
            Object value = MFReflect.getProperty(obj, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlDelete.getDeleteSql(tableName, where, argls.toArray());

        return this.execSql(sSQL);
    }

    public <T> int delete(Class<T> classType, String where, Object... args) throws SQLException {

        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);
        String tableName = cInfo.getTableName();

        String sSQL = MFSqlDelete.getDeleteSql(tableName, where, args);

        return this.execSql(sSQL);
    }

    public <T> int getCount(Class<T> classType) {
        return getCount(classType, MFString.Empty, new Object[1]);
    }

    public <T> int getCount(Class<T> classType, String where, Object... args) {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(classType);
        String tableName = cInfo.getTableViewName();

        String sWhere = MFString.Empty;
        if (MFString.notNullOrEmpty(where)) {
            sWhere = MFSqlSelect.WHERE + MFSqlSelect.getStaticSql(where, args);
        }
        String sSQL = String.format(MFSqlSelect.SELECT_COUNT, tableName, sWhere);

        Object iCount = -1;

        try {
            iCount = this.execScalar(sSQL);

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }

        BigDecimal i = (BigDecimal) iCount;
        return i.intValue();

    }

    public int getCount(String sql, Object... cmdParams) {
        Object iCount = -1;

        try {
            iCount = this.execScalar(sql, cmdParams);

        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }

        BigDecimal i = (BigDecimal) iCount;
        return i.intValue();

    }

    public int update(Object obj) throws Throwable {
        MFDataClassSQLAttInfo cInfo = MFDataTransfer.getClassSQLInfo(obj.getClass());
        String tableName = cInfo.getTableName();
        Map<String, String> ls = MFDataTransfer.getFieldPropList(obj);
        String strFields = MFString.Empty;

        List<Object> argls = new ArrayList<Object>();
        for (Map.Entry<String, String> item : ls.entrySet()) {
            if (MFString.notNullOrEmpty(strFields)) {
                strFields = strFields + MFSqlBase.COMMA + item.getKey() + MFSqlBase.EQUAL + MFSqlBase.PARAM;

            } else {
                strFields = item.getKey() + MFSqlBase.EQUAL + MFSqlBase.PARAM;

            }

            Object value = MFReflect.getProperty(obj, item.getValue());
            argls.add(value);
        }

        String where = MFString.Empty;
        Map<String, String> pls = MFDataTransfer.getPrimaryKeyFieldList(obj.getClass());

        for (Map.Entry<String, String> item : pls.entrySet()) {

            if (MFString.notNullOrEmpty(where)) {
                where = where + MFSqlBase.AND;
            }
            where = where + item.getKey() + "= ? ";
            Object value = MFReflect.getProperty(obj, item.getValue());
            argls.add(value);
        }

        String sSQL = MFSqlUpdate.getUpdateSql(tableName, strFields, where);

        return this.execSql(sSQL, argls.toArray());
    }

    public int updateDataRow(MFDataRow row) throws Exception {
        if (row.getState() == MFDataState.none) {
            return 0;
        }

        MFDataTable dataTable = row.getDataRows().getDataTable();
        List<String> modifyFields;
        List<String> insertFields;
        String sSQL;

        if (MFString.isNullOrEmpty(dataTable.getModifyFields())) {
            modifyFields = MFDbStruct.getTableFieldList(this, dataTable.getTableName());
        } else {
            modifyFields = dataTable.getModifyFieldList();
        }

        if (MFString.isNullOrEmpty(dataTable.getInsertFields())) {
            insertFields = modifyFields;
        } else {
            insertFields = dataTable.getInsertFieldList();
        }

        int i = 0;

        List<Object> modifyValues = getDataRowValue(row, modifyFields);
        List<Object> insertValues = getDataRowValue(row, insertFields);
        List<Object> pkValues = getDataRowValue(row, dataTable.getPrimaryFieldList());


        if (row.getState() == MFDataState.insert) {
            sSQL = MFSqlInsert.getInsertSqlParam(dataTable.getTableName(), insertFields);
            i = i + this.execSql(sSQL, insertValues.toArray());
        }

        if (row.getState() == MFDataState.modify) {
            if (dataTable.getUpdateType() == MFDataUpdateType.utAuto) {
                if (row.hasUpdateFields()) {
                    List<Object> mdfValues = getDataRowValue(row, row.getUpdateFieldList());
                    mdfValues.addAll(pkValues);
                    sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), row.getUpdateFieldList(), dataTable.getPrimaryFieldList());
                    i = i + this.execSql(sSQL, mdfValues.toArray());
                } else {
                    modifyValues.addAll(pkValues);
                    sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), modifyFields, dataTable.getPrimaryFieldList());
                    i = i + this.execSql(sSQL, modifyValues.toArray());
                }
            } else if (dataTable.getUpdateType() == MFDataUpdateType.utTable) {
                modifyValues.addAll(pkValues);
                sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), modifyFields, dataTable.getPrimaryFieldList());
                i = i + this.execSql(sSQL, modifyValues.toArray());
            } else if (dataTable.getUpdateType() == MFDataUpdateType.utRow) {
                List<Object> mdfValues = getDataRowValue(row, row.getUpdateFieldList());
                mdfValues.addAll(pkValues);
                sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), row.getUpdateFieldList(), dataTable.getPrimaryFieldList());
                i = i + this.execSql(sSQL, mdfValues.toArray());
            }

        }

//		if (row.getState()==MFDataState.modify)
//		{
//			modifyValues.addAll(pkValues);
//			sSQL=MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), modifyFields,dataTable.getPrimaryFieldList());
//			i=i+this.execSql(sSQL, modifyValues.toArray());
//		}

        if (row.getState() == MFDataState.delete) {
            sSQL = MFSqlDelete.getDeleteSqlParam(dataTable.getTableName(), dataTable.getPrimaryFieldList());
            i = i + this.execSql(sSQL, pkValues.toArray());
        }

        return i;
    }

    public int updateDataTable(MFDataTable dataTable) throws Exception {
        List<String> modifyFields;
        List<String> insertFields;
        String sSQL;

        if (MFString.isNullOrEmpty(dataTable.getModifyFields())) {
            modifyFields = MFDbStruct.getTableFieldList(this, dataTable.getTableName());
        } else {
            modifyFields = dataTable.getModifyFieldList();
        }

        if (MFString.isNullOrEmpty(dataTable.getInsertFields())) {
            insertFields = modifyFields;
        } else {
            insertFields = dataTable.getInsertFieldList();
        }

        int i = 0;
        for (MFDataRow row : dataTable.getRows()) {
            if (row.getState() == MFDataState.none) {
                continue;
            }


            List<Object> pkValues = getDataRowValue(row, dataTable.getPrimaryFieldList());
            List<Object> modifyValues = getDataRowValue(row, modifyFields);
            List<Object> insertValues = getDataRowValue(row, insertFields);

            if (row.getState() == MFDataState.insert) {
                sSQL = MFSqlInsert.getInsertSqlParam(dataTable.getTableName(), insertFields);
                i = i + this.execSql(sSQL, insertValues.toArray());
            }

            if (row.getState() == MFDataState.modify) {
                if (dataTable.getUpdateType() == MFDataUpdateType.utAuto) {
                    if (row.hasUpdateFields()) {
                        List<Object> mdfValues = getDataRowValue(row, row.getUpdateFieldList());
                        mdfValues.addAll(pkValues);
                        sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), row.getUpdateFieldList(), dataTable.getPrimaryFieldList());
                        i = i + this.execSql(sSQL, mdfValues.toArray());
                    } else {
                        modifyValues.addAll(pkValues);
                        sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), modifyFields, dataTable.getPrimaryFieldList());
                        i = i + this.execSql(sSQL, modifyValues.toArray());
                    }
                } else if (dataTable.getUpdateType() == MFDataUpdateType.utTable) {
                    modifyValues.addAll(pkValues);
                    sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), modifyFields, dataTable.getPrimaryFieldList());
                    i = i + this.execSql(sSQL, modifyValues.toArray());
                } else if (dataTable.getUpdateType() == MFDataUpdateType.utRow) {
                    List<Object> mdfValues = getDataRowValue(row, row.getUpdateFieldList());
                    mdfValues.addAll(pkValues);
                    sSQL = MFSqlUpdate.getUpdateSqlParam(dataTable.getTableName(), row.getUpdateFieldList(), dataTable.getPrimaryFieldList());
                    i = i + this.execSql(sSQL, mdfValues.toArray());
                }

            }

            if (row.getState() == MFDataState.delete) {
                sSQL = MFSqlDelete.getDeleteSqlParam(dataTable.getTableName(), dataTable.getPrimaryFieldList());
                i = i + this.execSql(sSQL, pkValues.toArray());
            }
        }

        return i;
    }

    public MFDataTable getDataTable(String sql, Object... cmdParams) throws SQLException, MFDataColumnException {
        ResultSet rs;
        if (transaction == null) {
            rs = MFDataManager.getResultSet(sql, cmdParams);
        } else {
            rs = MFDataManager.getResultSet(transaction.getConn(), sql, cmdParams);
        }

        if (rs != null) {
            MFDataTable dataTable;
            dataTable = MFDataTransfer.getDataTable(this, rs);
            return dataTable;
        } else {
            return null;
        }

    }

    public MFDataTable getDataTable(String sql) throws SQLException, MFDataColumnException {
        ResultSet rs = getResultSet(sql);
        if (rs != null) {
            MFDataTable dataTable;
            dataTable = MFDataTransfer.getDataTable(this, rs);
            return dataTable;
        } else {
            return null;
        }

    }

    public ResultSet getResultSet(String sql) throws SQLException {
        ResultSet rs;
        logger.debug(sql);
        if (transaction == null) {
            rs = MFDataManager.getResultSet(sql);
        } else {
            rs = MFDataManager.getResultSet(transaction.getConn(), sql);
        }
        return rs;

    }

    public void closeAuto(ResultSet rs) throws SQLException {
        if (transaction == null) {
            MFDataManager.closeResultSet(rs);
        } else {
            MFDataManager.close(rs);
            MFDataManager.close(rs.getStatement());
        }

    }

    public void close(ResultSet rs) throws SQLException {
        MFDataManager.close(rs);
        MFDataManager.close(rs.getStatement());

    }

    public ResultSet getResultSet(String cmdText, Object... cmdParams) throws SQLException {
        ResultSet rs;
        if (transaction == null) {
            rs = MFDataManager.getResultSet(cmdText, cmdParams);
        } else {
            rs = MFDataManager.getResultSet(transaction.getConn(), cmdText, cmdParams);
        }
        return rs;
    }

    public int execSql(String sql) throws SQLException {
        int count;
        logger.debug(sql);
        if (transaction == null) {
            count = MFDataManager.execSql(sql);
        } else {
            count = MFDataManager.execSql(transaction.getConn(), sql);
        }
        return count;

    }

    public Object execScalar(String sql) throws SQLException {
        Object obj;
        if (transaction == null) {
            obj = MFDataManager.execScalar(sql);
        } else {
            obj = MFDataManager.execScalar(transaction.getConn(), sql);
        }
        return obj;

    }

    public Object execScalar(String sql, Object... cmdParams) throws SQLException {
        Object obj;
        if (transaction == null) {
            obj = MFDataManager.execScalar(sql, cmdParams);
        } else {
            obj = MFDataManager.execScalar(transaction.getConn(), sql, cmdParams);
        }
        return obj;

    }

    public int execSql(String sql, Object... cmdParams) throws SQLException {
        int count;
        if (transaction == null) {
            count = MFDataManager.execSql(sql, cmdParams);
        } else {
            count = MFDataManager.execSql(transaction.getConn(), sql, cmdParams);
        }
        return count;
    }

    //调用存储过程
    //需要 ? 参数的 SQL 语句  "{? = call sp(?)}";
    public CallableStatement prepareCall(String sql) throws SQLException {
        return transaction.getConn().prepareCall(sql);
    }

    public MFDataTable callSpQueryTable(String cmdText, Object... cmdParams) throws SQLException, MFDataColumnException {
        ResultSet rs = callSpQuery(cmdText, cmdParams);
        return MFDataTransfer.getDataTable(this, rs);
    }

    public ResultSet callSpQuery(String cmdText, Object... cmdParams) throws SQLException {
        ResultSet result = null;
        result = MFDataManager.callSpQuery(transaction.getConn(), cmdText, cmdParams);
        return result;
    }

    public Object callSpReturn(String cmdText, int returnType, Object... cmdParams) throws SQLException {
        return MFDataManager.callSpReturn(transaction.getConn(), cmdText, returnType, cmdParams);

    }

    public Object callSpReturnLast(String cmdText, int returnType, Object... cmdParams) throws SQLException {
        return MFDataManager.callSpReturnLast(transaction.getConn(), cmdText, returnType, cmdParams);

    }

    public boolean callSp(String cmdText, Object... cmdParams) throws SQLException {
        boolean result = false;
        result = MFDataManager.callSp(transaction.getConn(), cmdText, cmdParams);
        return result;
    }

    public void setTransaction(MFTransaction transaction) {
        this.transaction = transaction;
    }


    public MFTransaction getTransaction() {
        return transaction;
    }

    public void setDbName(String dbName) {
        this.dbName = dbName;
    }

    public String getDbName() {
        return dbName;
    }
}
